#include "netassist.h"
#include "ui_netassist.h"
#include <QMessageBox>
NetAssist::NetAssist(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::NetAssist)
{
    ui->setupUi(this);

    // 连接按钮状态初始化
    this->_Flag_Port_ConnectBreak = false;
    // 自动换行按钮状态初始化
    this->_Flag_Auto_Line = false;
    // 十六进制显示按钮状态初始化
    this->_Flag_Hex_Display = false;
    // 十六进制发送按钮状态初始化
    this->_Flag_Hex_Send = false;
    // 群发标志位状态初始化
    this->_Group_Send = false;
    // 显示接收时间按钮状态初始化
    this->_Display_Time = false;
    // 发送按钮状态初始化
    this->_Send = false;
    // 暂停接收显示初始化
    this->_Stop_Display = false;
    // 循环周期发送按钮状态初始化
    this->_Loop_Send = false;
    // 启用文件数据源按钮状态初始化
    this->_Send_File = false;
    // 保存数据按钮状态初始化
    this->_Save_Data = false;
    // 接收文件转向按钮状态初始化
    this->_Recv_File = false;
    // 发送按钮状态初始化
    this->_Send = false;
    // 远程 ip 地址与端口号状态初始化
    this->_flag_IpPort = false;
    // 发送计数初始化
    this->_TX_Count = 0;
    // 接收计数初始化
    this->_RX_Count = 0;

    // 初始化下拉列表框
    ui->comboBox_NetType->addItem("UDP");
    ui->comboBox_NetType->addItem("TCP Client");
    ui->comboBox_NetType->addItem("TCP Server");

    // 初始化发送、接收计数显示
    ui->label_TX->setText(QString("TX:%1").arg(this->_TX_Count));
    ui->label_RX->setText(QString("RX:%1").arg(this->_RX_Count));

    // 初始化发送按钮
    ui->pushButton_Send->setEnabled(false);

    // 初始化循环发送时间 1000 ms
    this->_Send_Time = QString("1000");

    // 获取本地 ip 地址
    Get_Ip();

}

NetAssist::~NetAssist()
{
    delete ui;
}

// 获取本地 ip 地址
void NetAssist::Get_Ip()
{
    QString ip_address;
    QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
    for (int i = 0; i < ipAddressesList.size(); ++i)
    {
        if (ipAddressesList.at(i) != QHostAddress::LocalHost && ipAddressesList.at(i).toIPv4Address())
        {
            ip_address = ipAddressesList.at(i).toString();
        }
    }
    if (ip_address.isEmpty())
    {
        ip_address = QHostAddress(QHostAddress::LocalHost).toString();
    }
    // 显示本地 ip 地址与端口默认值
    ui->lineEdit_Ip->setText(ip_address);
    ui->lineEdit_Port->setText("8081");
}

// UDP 绑定
void NetAssist::UDP_Bind()
{
    // 创建 UDP 发送套接字
    this->_UdpSocketSend = new QUdpSocket(this);
    // 创建 UDP 接收套接字
    this->_UdpSocketRecv = new QUdpSocket(this);
    // 接收绑定端口号，发送不用绑定
    this->_UdpSocketRecv->bind(QHostAddress::AnyIPv4,this->_MyPort,QUdpSocket::ShareAddress);
    // 信号槽设置（UDP 接收）
    connect(this->_UdpSocketRecv,SIGNAL(readyRead()),this,SLOT(UDP_Recv()));
}

// UDP 发送信息
void NetAssist::UDP_Send()
{
    // 获取要发送的信息
    QString msg = ui->textEdit_Send->toPlainText();
    // 是否启用文件数据源
    if (this->_Send_File)
    {
        // 文件数据源赋值
        msg = this->_Send_File_Data;
    }
    // 十六进制发送
    if (this->_Flag_Hex_Send)
    {
        // 发送数据
        _UdpSocketSend->writeDatagram(this->_Hex_Send_Str.toUtf8(),QHostAddress(this->_Ip),this->_Port);
        // 发送计数并显示
        this->_TX_Count += this->_Hex_Send_Str.size();
        ui->label_TX->setText(QString("TX:%1").arg(this->_TX_Count));
    }
    else
    {
        // 发送数据
        _UdpSocketSend->writeDatagram(msg.toUtf8(),QHostAddress(this->_Ip),this->_Port);
        // 发送计数并显示
        this->_TX_Count += msg.size();
        ui->label_TX->setText(QString("TX:%1").arg(this->_TX_Count));
    }
}

// TCP 客户端初始化
void NetAssist::TCP_Client_Init()
{
    // 创建 TCP 客户端套接字
    this->_TcpClientSocket = new QTcpSocket(this);
    // 连接服务器
    this->_TcpClientSocket->connectToHost(QHostAddress(this->_MyIp),this->_MyPort);
    // 信号槽设置（TCP 客户端连接）
    connect(this->_TcpClientSocket,&QTcpSocket::connected,[=]()
    {
        // 判断是否接收转向文件
        if (this->_Recv_File)
        {
            // 保存转向数据
            this->_Recv_File_Data = "成功和服务器建立好连接。";
            // 接收转向文件处理
            Recv_File_Data();
        }
        else
        {
            ui->textEdit_Recv->append("成功和服务器建立好连接。\n");
        }
    });
    // 信号槽设置（TCP 客户端接收）
    connect(this->_TcpClientSocket,SIGNAL(readyRead()),this,SLOT(TCP_Client_Recv()));
}

// TCP 客户端发送信息
void NetAssist::TCP_Client_Send()
{
    // 获取要发送的信息
    QString msg = ui->textEdit_Send->toPlainText();
    // 是否启用文件数据源
    if (this->_Send_File)
    {
        // 文件数据源赋值
        msg = this->_Send_File_Data;
    }
    // 十六进制发送
    if (this->_Flag_Hex_Send)
    {
        // 发送给服务器
        this->_TcpClientSocket->write(this->_Hex_Send_Str.toUtf8());
        // 发送计数并显示
        this->_TX_Count += this->_Hex_Send_Str.size();
        ui->label_TX->setText(QString("TX:%1").arg(this->_TX_Count));
    }
    else
    {
        // 发送给服务器
        this->_TcpClientSocket->write(msg.toUtf8());
        // 发送计数并显示
        this->_TX_Count += msg.size();
        ui->label_TX->setText(QString("TX:%1").arg(this->_TX_Count));
    }
}

// TCP 服务器初始化
void NetAssist::TCP_Server_Init()
{
    // 创建 TCP 服务器套接字
    this->_TcpServerSocket = new QTcpServer(this);
    // 监听(所有 ip 地址，端口号唯一)
    this->_TcpServerSocket->listen(QHostAddress::Any,this->_MyPort);
    // 信号槽设置（TCP 新客户端连接）
    connect(this->_TcpServerSocket,SIGNAL(newConnection()),this,SLOT(New_Client_Connect()));
}

// TCP 服务器发送信息
void NetAssist::TCP_Server_Send()
{
    // 获取文本发送框内容
    QString str = ui->textEdit_Send->toPlainText();
    // 是否启用文件数据源
    if (this->_Send_File)
    {
        // 文件数据源赋值
        str = this->_Send_File_Data;
    }
    // 遍历容器选择客户端
    for (int i = 0; i < this->_All_ClientSocket.size(); i++)
    {
        // 群发
        if (this->_Group_Send)
        {
            // 十六进制发送
            if (this->_Flag_Hex_Send)
            {
                //给指定客户端发送数据，使用套接字是TCPSocket
                this->_All_ClientSocket[i]->write(this->_Hex_Send_Str.toUtf8().data());
                // 发送计数并显示
                this->_TX_Count += this->_Hex_Send_Str.size();
                ui->label_TX->setText(QString("TX:%1").arg(this->_TX_Count));
            }
            else
            {
                //给指定客户端发送数据，使用套接字是TCPSocket
                this->_All_ClientSocket[i]->write(str.toUtf8().data());
                // 发送计数并显示
                this->_TX_Count += str.size();
                ui->label_TX->setText(QString("TX:%1").arg(this->_TX_Count));
            }
        }
        // ip 地址字符串
        QString ip = this->_All_ClientSocket[i]->peerAddress().toString();
        // 切割字符串
        QStringList list = ip.split("::ffff:");
        // ip 地址与端口号相同
        if (QString::localeAwareCompare(list[1],this->_Ip) == 0 && this->_All_ClientSocket[i]->peerPort() == this->_Port)
        {
            // 十六进制发送
            if (this->_Flag_Hex_Send)
            {
                //给指定客户端发送数据，使用套接字是TCPSocket
                this->_All_ClientSocket[i]->write(this->_Hex_Send_Str.toUtf8().data());
                // 发送计数并显示
                this->_TX_Count += this->_Hex_Send_Str.size();
                ui->label_TX->setText(QString("TX:%1").arg(this->_TX_Count));
            }
            else
            {
                //给指定客户端发送数据，使用套接字是TCPSocket
                this->_All_ClientSocket[i]->write(str.toUtf8().data());
                // 发送计数并显示
                this->_TX_Count += str.size();
                ui->label_TX->setText(QString("TX:%1").arg(this->_TX_Count));
            }
        }
    } 
}

// 字符串转换十六进制
QString NetAssist::String_To_Hex(QString Data)
{
    // 字符串变量
    QString strDis;
    // 转换为十六进制
    QString str = Data.toUtf8().toHex().data();
    // 转换为大写
    str = str.toUpper();
    // 填加空格
    for(int i = 0; i < str.length(); i += 2)
    {
        QString tmp = str.mid(i,2);
        strDis += tmp;
        strDis += " ";
    }
    return strDis;
}

// 根据网络类型发送信息
void NetAssist::NetType_Send()
{
    // 判断远程 ip 地址与端口号是否为空
    if (this->_flag_IpPort)
    {
        // 用消息盒子提示
        QMessageBox::warning(this,"警告","没有远程主机/客户端。", QMessageBox::Ok);
        return;
    }
    // 根据网络类型选择发送
    switch (this->_NetType)
    {
        case UDP :
            // UDP 发送信息
            UDP_Send();
            break;

        case TCPClient :
            // TCP 客户端发送信息
            TCP_Client_Send();
            break;

        case TCPServer :
            // TCP 服务器发送信息
            TCP_Server_Send();
            break;
    }
}

// 根据功能显示接收信息
void NetAssist::Function_Display(QString str)
{
    // 字符串变量
    QString New_str = str;
    // 十六进制显示
    if (this->_Flag_Hex_Display)
    {
        New_str = String_To_Hex(str);
    }
    // 显示接收时间
    if (this->_Display_Time)
    {
        // 创建日期时间对象
        QDateTime Date_Time = QDateTime::currentDateTime();
        // 转换为字符串
        QString Str_Date_Time = Date_Time.toString("yyyy-MM-dd hh:mm:ss:zzz");
        // 拼接
        New_str = QString("【%1】：%2").arg(Str_Date_Time).arg(New_str);
    }
    // 换行
    if (this->_Flag_Auto_Line)
    {
        // 暂停接收显示
        if (!this->_Stop_Display)
        {
            // 追加显示
            ui->textEdit_Recv->append(New_str.toUtf8());
        }
        // 接收计数并显示
        this->_RX_Count += str.size();
        ui->label_RX->setText(QString("RX:%1").arg(this->_RX_Count));
    }
    else
    {
        // 暂停接收显示
        if (!this->_Stop_Display)
        {
            // 追加显示
            ui->textEdit_Recv->insertPlainText(New_str.toUtf8());
        }
        // 接收计数并显示
        this->_RX_Count += str.size();
        ui->label_RX->setText(QString("RX:%1").arg(this->_RX_Count));
    }
}

// 拼接 ip 地址与端口号
QString NetAssist::Joint_Ip_Port(QTcpSocket *Socket)
{
    // 获取 IP 地址和端口号
    QString ip = Socket->peerAddress().toString();
    quint16 port = Socket->peerPort();
    // 切割字符串
    QStringList list = ip.split("::ffff:");
    // 拼接的 ip 地址与端口号
    QString IpPort = QString("%1:%2").arg(list[1]).arg(port);
    return IpPort;
}

// 文件载入处理
void NetAssist::Read_File_Data()
{
    // 初始化数据
    this->_Send_File_Data.clear();
    // 获取文件绝对路径
    QString filename = QFileDialog::getOpenFileName(this, "选择一个文件载入数据发送", nullptr, "All files(*);;Text files(*.txt)");
    // 创建文件对象
    QFile file(filename);
    // 判断文件是否存在
    if (!file.exists())
    {
        // 消息盒子
        QMessageBox::warning(this, "警告", "文件不存在！");
        return;
    }
    // 判断文件能否打开
    if (!file.open(QIODevice::ReadOnly))
    {
        // 消息盒子
        QMessageBox::warning(this, "警告", "文件打不开！");
        return;
    }
    // 循环读取文件内容
    while (!file.atEnd())
    {
        // 一行行读取
        this->_Send_File_Data.append(file.readLine(512));
    }
    // 释放文件对象
    file.close();
    // 消息盒子
    QMessageBox::information(this, "提示", "文件载入成功！");
}

// 启用文件数据源处理
void NetAssist::Send_File_Data()
{
    // 初始化数据
    this->_Send_File_Data.clear();
    // 获取文件绝对路径
    QString filename = QFileDialog::getOpenFileName(this, "选择一个文件作为文件数据源", nullptr, "All files(*);;Text files(*.txt)");
    // 创建文件对象
    QFile file(filename);
    // 判断文件是否存在
    if (!file.exists())
    {
        // 消息盒子
        QMessageBox::warning(this, "警告", "文件不存在！");
        // 不勾选
        ui->checkBox_SendFile->setChecked(false);
        return;
    }
    // 判断文件能否打开
    if (!file.open(QIODevice::ReadOnly))
    {
        // 消息盒子
        QMessageBox::warning(this, "警告", "文件打不开！");
        // 不勾选
        ui->checkBox_SendFile->setChecked(false);
        return;
    }
    // 循环读取文件内容
    while (!file.atEnd())
    {
        // 一行行读取
        this->_Send_File_Data.append(file.readLine(512));
    }
    // 释放文件对象
    file.close();
    // 消息盒子
    QMessageBox::information(this, "提示", "启用文件数据源成功！");
    // 发送文本框显示文件路径
    ui->textEdit_Send->setText("启用外部文件数据源：");
    ui->textEdit_Send->append(filename + "...");
    // 发送文本框不可编辑
    ui->textEdit_Send->setEnabled(false);
}

// 保存数据处理
void NetAssist::Write_File_Data()
{
    // 获取接收文本框数据
    QString str = ui->textEdit_Recv->toPlainText();
    // 获取文件绝对路径
    QString filename = QFileDialog::getOpenFileName(this, "选择或创建一个文件保存数据", nullptr, "All files(*);;Text files(*.txt)");
    // 创建文件对象
    QFile file(filename);
    // 判断文件是否存在
    if (!file.exists())
    {
        // 消息盒子
        QMessageBox::warning(this, "警告", "文件不存在！");
        return;
    }
    // 判断文件能否打开
    if (!file.open(QIODevice::WriteOnly))
    {
        // 消息盒子
        QMessageBox::warning(this, "警告", "文件打不开！");
        return;
    }
    // 写入数据
    this->_Send_File_Data.append(file.write(str.toUtf8() + "\n"));
    // 释放文件对象
    file.close();
    // 消息盒子
    QMessageBox::information(this, "提示", "保存数据成功！");
}

// 接收转向文件处理
void NetAssist::Recv_File_Data()
{
    // 创建文件对象
    QFile file(this->_Recv_FileName);
    // 判断文件是否存在、能否打开
    if (file.exists() && file.open(QIODevice::WriteOnly | QIODevice::Append))
    {
        // 写入数据
        file.write(this->_Recv_File_Data.toUtf8() + "\n");
        // 释放文件对象
        file.close();
    }
}

// UDP 接收信息
void NetAssist::UDP_Recv()
{
    //判断有数据报接收到
    while (_UdpSocketRecv->hasPendingDatagrams())
    {
        // 判断远程 ip 地址与端口号状态
        bool flag = true;
        // 创建数据报数组
        QByteArray datagram;
        // 创建接收 ip 地址与端口号变量
        QHostAddress Recvip;
        quint16 Recvport;
        //设置字节数组大小
        datagram.resize(_UdpSocketRecv->pendingDatagramSize());
        //读取数据
        _UdpSocketRecv->readDatagram(datagram.data(),datagram.size(),&Recvip,&Recvport);
        // 切割字符串
        QStringList list = Recvip.toString().split("::ffff:");
        // 拼接远程 ip 地址与端口号
        QString IpPort = QString("%1:%2").arg(list[0]).arg(Recvport);
        // 遍历容器判断该 ip 地址与端口号是否存在
        for (int i = 0; i < this->_IpPort.size(); i++)
        {
            // ip 地址与端口号相同
            if (QString::localeAwareCompare(this->_IpPort[i], IpPort) == 0)
            {
                // 存在
                flag = false;
            }
        }
        // 不存在
        if (flag)
        {
            // 排除本地端口号
            if (this->_MyPort != Recvport)
            {
                // 保存进容器
                this->_IpPort.append(IpPort);
                // 加入下拉列表框 选择远程主机 ip 地址与端口号
                ui->comboBox_IpPort->addItem(IpPort);
            }
        }
        // 判断是否同一个远程主机多次发送信息
        if (QString::localeAwareCompare(this->_Last_IpPort,IpPort) != 0)
        {
            // 拼接远程主机连接信息
            QString temp = QString("远程主机【%1】：").arg(IpPort);
            // 判断是否接收转向文件
            if (this->_Recv_File)
            {
                // 保存转向数据
                this->_Recv_File_Data = temp;
                // 接收转向文件处理
                Recv_File_Data();
            }
            else
            {
                // 显示远程主机信息
                ui->textEdit_Recv->append(temp + "\n");
            }
        }
        // 记录发送者 ip 地址与端口号
        this->_Last_IpPort.clear();
        this->_Last_IpPort = IpPort;
        //将读取的数据转化为字符串
        QString str = datagram.data();
        // 判断是否接收转向文件
        if (this->_Recv_File)
        {
            // 保存转向数据
            this->_Recv_File_Data = str;
            // 接收转向文件处理
            Recv_File_Data();
        }
        else
        {
            // 根据功能显示接收信息
            Function_Display(str);
        }
    }
}

// TCP 客户端接收信息
void NetAssist::TCP_Client_Recv()
{
    // 接收信息
    QString str = this->_TcpClientSocket->readAll();
    // 判断是否接收转向文件
    if (this->_Recv_File)
    {
        // 保存转向数据
        this->_Recv_File_Data = str;
        // 接收转向文件处理
        Recv_File_Data();
    }
    else
    {
        // 根据功能显示接收信息
        Function_Display(str);
    }
}

// 新的客户端连接服务器
void NetAssist::New_Client_Connect()
{
    // 判断客户端状态
    bool flag = false;
    // 清空上一个客户端套接字
    this->_TcpClientSocket = nullptr;
    //获取新连接客户端的套接字
    this->_TcpClientSocket = this->_TcpServerSocket->nextPendingConnection();
    // 拼接新连接客户端的 ip 地址与端口号
    QString str = QString("客户端【%1】：连接成功").arg(Joint_Ip_Port(this->_TcpClientSocket));
    // 遍历容器判断该客户端是否存在
    for (int i = 0; i < this->_All_ClientSocket.size(); i++)
    {
        // 客户端套接字 ip 地址与端口号相同
        if (QString::localeAwareCompare(this->_All_ClientSocket[i]->peerAddress().toString(),
                                        this->_TcpClientSocket->peerAddress().toString()) == 0
                                        && this->_All_ClientSocket[i]->peerPort() == this->_TcpClientSocket->peerPort())
        {
            // 存在
            flag = true;
        }
    }
    // 不存在
    if (!flag)
    {
        // 保存进容器
        this->_All_ClientSocket.push_back(this->_TcpClientSocket);
        // 加入下拉列表框 选择远程主机 ip 地址与端口号
        ui->comboBox_IpPort->addItem(Joint_Ip_Port(this->_TcpClientSocket));
    }
    // 判断是否接收转向文件
    if (this->_Recv_File)
    {
        // 保存转向数据
        this->_Recv_File_Data = str;
        // 接收转向文件处理
        Recv_File_Data();
    }
    else
    {
        // 接收文本框显示
        ui->textEdit_Recv->append(str);
    }
    // 信号槽设置（TCP 服务器接收信息）
    connect(this->_TcpClientSocket,SIGNAL(readyRead()),SLOT(TCP_Server_Recv()));
    // 信号槽设置（客户端断开连接）
    connect(this->_TcpClientSocket,SIGNAL(disconnected()),this,SLOT(Client_Break_Connect()));
}

// TCP 服务器接收信息
void NetAssist::TCP_Server_Recv()
{
    // 客户端对象（谁给服务器发送信息就是谁）
    QTcpSocket *obj = (QTcpSocket*)sender();
    // 读取接收信息
    QString msg = obj->readAll();
    // 拼接连接客户端的 ip 地址与端口号
    QString IpPort = Joint_Ip_Port(obj);
    // 判断是否同一个客户端多次发送信息
    if (QString::localeAwareCompare(this->_Last_IpPort,IpPort) != 0)
    {
        // 拼接客户端连接信息
        QString temp = QString("客户端【%1】：").arg(IpPort);
        // 判断是否接收转向文件
        if (this->_Recv_File)
        {
            // 保存转向数据
            this->_Recv_File_Data = temp;
            // 接收转向文件处理
            Recv_File_Data();
        }
        else
        {
            // 显示客户端信息
            ui->textEdit_Recv->append(temp + "\n");
        }
    }
    // 记录发送者 ip 地址与端口号
    this->_Last_IpPort.clear();
    this->_Last_IpPort = IpPort;
    // 判断是否接收转向文件
    if (this->_Recv_File)
    {
        // 保存转向数据
        this->_Recv_File_Data = msg;
        // 接收转向文件处理
        Recv_File_Data();
    }
    else
    {
        // 根据功能显示接收信息
        Function_Display(msg);
    }
}

// 客户端断开连接
void NetAssist::Client_Break_Connect()
{
    // 掉线对象（谁掉线就是谁）
    QTcpSocket *obj = (QTcpSocket*)sender();
    // 获取连接客户端的 IP 地址和端口号
    QString ip = obj->peerAddress().toString();
    quint16 port = obj->peerPort();
    // 切割字符串
    QStringList list = ip.split("::ffff:");
    // 拼接连接客户端的 IP 地址和端口号
    QString temp = QString("客户端【%1:%2】：已断开连接").arg(list[1]).arg(port);
    // 判断是否接收转向文件
    if (this->_Recv_File)
    {
        // 保存转向数据
        this->_Recv_File_Data = temp;
        // 接收转向文件处理
        Recv_File_Data();
    }
    else
    {
        // 接收文本框显示
        ui->textEdit_Recv->append(temp);
    }
}

// 下拉列表框 选择协议类型
void NetAssist::on_comboBox_NetType_currentIndexChanged(int index)
{
    // 根据下拉列表框索引获取协议类型
    switch (index)
    {
        case UDP :
            // 清除本地 ip 地址标签
            ui->label_Ip->clear();
            // 清除本地端口号标签
            ui->label_Port->clear();
            // 清除发送者 ip 地址与端口号
            this->_Last_IpPort.clear();
            // 清除远程主机 ip 地址与端口号容器
            this->_IpPort.clear();
            // 清除客户端容器
            this->_All_ClientSocket.clear();
            // 清除下拉列表框
            ui->comboBox_IpPort->clear();
            // 清除远程主机 ip 地址与端口号
            this->_IpPort.clear();
            // 远程主机 ip 地址与端口号下拉列表框可选
            ui->comboBox_IpPort->setEnabled(true);
            // 远程主机 ip 地址与端口号下拉列表框清除按钮可选
            ui->pushButton_Clear->setEnabled(true);
            // 远程主机 ip 地址与端口号下拉列表框可编辑
            ui->comboBox_IpPort->setEditable(true);
            // 按钮设置文字
            ui->pushButton_ConnectBreak->setText("打开");
            // 本地 ip 地址标签设置文字
            ui->label_Ip->setText("本地主机 ip 地址：");
            // 本地 ip 地址标签设置文字
            ui->label_Port->setText("本地主机端口号：");
            // 远程主机标签设置文字
            ui->label_4->setText("远程主机：");
            // 保存协议类型
            this->_NetType = index;
            break;

        case TCPClient :
            // 清除本地 ip 地址标签
            ui->label_Ip->clear();
            // 清除本地端口号标签
            ui->label_Port->clear();
            // 清除发送者 ip 地址与端口号
            this->_Last_IpPort.clear();
            // 清除远程主机 ip 地址与端口号容器
            this->_IpPort.clear();
            // 清除客户端容器
            this->_All_ClientSocket.clear();
            // 清除下拉列表框
            ui->comboBox_IpPort->clear();
            // 远程主机 ip 地址与端口号下拉列表框不可选
            ui->comboBox_IpPort->setEnabled(false);
            // 远程主机 ip 地址与端口号下拉列表框清除按钮不可选
            ui->pushButton_Clear->setEnabled(false);
            // 按钮设置文字
            ui->pushButton_ConnectBreak->setText("连接");
            // 本地 ip 地址标签设置文字
            ui->label_Ip->setText("服务器 ip 地址：");
            // 本地 ip 地址标签设置文字
            ui->label_Port->setText("服务器端口号：");
            // 保存协议类型
            this->_NetType = index;
            break;

        case TCPServer :
            // 清除本地 ip 地址标签
            ui->label_Ip->clear();
            // 清除本地端口号标签
            ui->label_Port->clear();
            // 清除发送者 ip 地址与端口号
            this->_Last_IpPort.clear();
            // 清除远程主机 ip 地址与端口号容器
            this->_IpPort.clear();
            // 清除客户端容器
            this->_All_ClientSocket.clear();
            // 清除下拉列表框
            ui->comboBox_IpPort->clear();
            // 远程主机 ip 地址与端口号下拉列表框可选
            ui->comboBox_IpPort->setEnabled(true);
            // 远程主机 ip 地址与端口号下拉列表框清除按钮可选
            ui->pushButton_Clear->setEnabled(true);
            // 远程主机 ip 地址与端口号下拉列表框不可编辑
            ui->comboBox_IpPort->setEditable(false);
            // 按钮设置文字
            ui->pushButton_ConnectBreak->setText("打开");
            // 本地 ip 地址标签设置文字
            ui->label_Ip->setText("本地主机 ip 地址：");
            // 本地 ip 地址标签设置文字
            ui->label_Port->setText("本地主机端口号：");
            // 远程主机标签设置文字
            ui->label_4->setText("客户端：");
            // 保存协议类型
            this->_NetType = index;
            break;
    }
}

// 连接按钮
void NetAssist::on_pushButton_ConnectBreak_clicked()
{
    // 连接按钮状态每次取反
    this->_Flag_Port_ConnectBreak = !this->_Flag_Port_ConnectBreak;
    // 连接
    if (this->_Flag_Port_ConnectBreak)
    {
        // 发送按钮可用
        ui->pushButton_Send->setEnabled(true);
        // 设置下拉列表框不可选
        ui->comboBox_NetType->setEnabled(false);
        // 设置本地 ip 地址不可编辑
        ui->lineEdit_Ip->setEnabled(false);
        // 设置本地端口号不可编辑
        ui->lineEdit_Port->setEnabled(false);
        // 设置按钮为绿色
        ui->pushButton_ConnectBreak->setStyleSheet("QPushButton{background-color: rgb(0, 255, 0);}");
        // 根据网络类型进行连接
        switch (this->_NetType)
        {
            case UDP :
                // 按钮设置文字
                ui->pushButton_ConnectBreak->setText("关闭");
                // 获取 IP 地址与端口号
                this->_MyIp = ui->lineEdit_Ip->text();
                this->_MyPort = ui->lineEdit_Port->text().toInt();
                // 下拉列表框显示远程 ip 地址与端口默认值
                ui->comboBox_IpPort->addItem(QString("%1:%2").arg(this->_MyIp).arg(this->_MyPort));
                // 保存进容器
                this->_IpPort.append(QString("%1:%2").arg(this->_MyIp).arg(this->_MyPort));
                // UDP 绑定
                UDP_Bind();
                break;

            case TCPClient :
                // 按钮设置文字
                ui->pushButton_ConnectBreak->setText("断开");
                // 获取 IP 地址与端口号
                this->_MyIp = ui->lineEdit_Ip->text();
                this->_MyPort = ui->lineEdit_Port->text().toInt();
                // 下拉列表框显示远程 ip 地址与端口默认值
                ui->comboBox_IpPort->addItem(QString("%1:%2").arg(this->_MyIp).arg(this->_MyPort));
                // TCP 客户端初始化
                TCP_Client_Init();
                break;

            case TCPServer :
                // 按钮设置文字
                ui->pushButton_ConnectBreak->setText("关闭");
                // 获取 IP 地址与端口号
                this->_MyIp = ui->lineEdit_Ip->text();
                this->_MyPort = ui->lineEdit_Port->text().toInt();
                // 下拉列表框显示默认值
                ui->comboBox_IpPort->addItem(" ALL Connections");
                // TCP 服务器初始化
                TCP_Server_Init();
                break;
        }
    }
    // 断开
    else
    {
        // 发送按钮不可用
        ui->pushButton_Send->setEnabled(false);
        // 设置下拉列表框可选
        ui->comboBox_NetType->setEnabled(true);
        // 设置本地 ip 地址可编辑
        ui->lineEdit_Ip->setEnabled(true);
        // 设置本地端口号可编辑
        ui->lineEdit_Port->setEnabled(true);
        // 设置按钮原来颜色
        ui->pushButton_ConnectBreak->setStyleSheet("QPushButton{background-color: rgb(215, 215, 215);}");
        // 根据网络类型进行连接
        switch (this->_NetType)
        {
            case UDP :
                // 按钮设置文字
                ui->pushButton_ConnectBreak->setText("打开");
                // 解除端口号绑定
                this->_UdpSocketRecv->abort();
                // 释放套接字
                this->_UdpSocketSend->close();
                this->_UdpSocketRecv->close();
                // 置位变量
                this->_MyIp.clear();
                this->_MyPort = 0;
                this->_IpPort.clear();
                this->_Last_IpPort.clear();
                ui->comboBox_IpPort->clear();
                break;

            case TCPClient :
                // 按钮设置文字
                ui->pushButton_ConnectBreak->setText("连接");
                // 主动和服务器断开连接
                this->_TcpClientSocket->disconnectFromHost();
                // 释放套接字
                this->_TcpClientSocket->close();
                // 置位变量
                this->_MyIp.clear();
                this->_MyPort = 0;
                this->_Last_IpPort.clear();
                ui->comboBox_IpPort->clear();
                break;

            case TCPServer :
                // 按钮设置文字
                ui->pushButton_ConnectBreak->setText("打开");
                // 释放套接字
                this->_TcpClientSocket = nullptr;
                this->_TcpServerSocket->close();
                // 遍历容器客户端和服务器断开连接
                for (int i = 0; i < this->_All_ClientSocket.size(); i++)
                {
                    this->_All_ClientSocket[i]->disconnectFromHost();
                }
                // 置位变量
                this->_MyIp.clear();
                this->_MyPort = 0;
                this->_Last_IpPort.clear();
                this->_All_ClientSocket.clear();
                ui->comboBox_IpPort->clear();
                break;
        }
    }
    qDebug() << this->_Flag_Port_ConnectBreak;
}

// 发送按钮
void NetAssist::on_pushButton_Send_clicked()
{
    // 是否循环发送
    if (this->_Loop_Send)
    {
        // 点击按钮改变状态
        this->_Send = !this->_Send;
        // 循环周期 xxx ms
        this->_Timer->start(this->_Send_Time.toInt());
        if (this->_Send)
        {
            // 发送区相关设置不使能
            ui->checkBox_SendFile->setEnabled(false);
            ui->checkBox_HexSend->setEnabled(false);
            ui->checkBox_SendTime->setEnabled(false);
            ui->lineEdit_SendTime->setEnabled(false);
            ui->pushButton_SendData->setEnabled(false);
            ui->pushButton_ClearSend->setEnabled(false);
            ui->comboBox_IpPort->setEnabled(false);
            ui->pushButton_Clear->setEnabled(false);
            ui->textEdit_Send->setEnabled(false);
            ui->pushButton_Send->setText("停止发送");
        }
        else
        {
            // 发送区相关设置使能
            ui->checkBox_SendFile->setEnabled(true);
            ui->checkBox_HexSend->setEnabled(true);
            ui->checkBox_SendTime->setEnabled(true);
            ui->lineEdit_SendTime->setEnabled(true);
            ui->pushButton_SendData->setEnabled(true);
            ui->pushButton_ClearSend->setEnabled(true);
            ui->comboBox_IpPort->setEnabled(true);
            ui->pushButton_Clear->setEnabled(true);
            ui->textEdit_Send->setEnabled(true);
            ui->pushButton_Send->setText("发送");
        }
    }
    else
    {
        // 根据网络类型发送信息
        NetType_Send();
    }

}

// 清除接收区
void NetAssist::on_pushButton_ClearRecv_clicked()
{
    ui->textEdit_Recv->clear();
}

// 清除发送区
void NetAssist::on_pushButton_ClearSend_clicked()
{
    ui->textEdit_Send->clear();
}

// 自动换行
void NetAssist::on_checkBox_clicked()
{
    // 每次点击改变状态
    this->_Flag_Auto_Line = !this->_Flag_Auto_Line;
}

// 十六进制显示
void NetAssist::on_checkBox_HexDisplay_clicked()
{
    // 每次点击改变状态
    this->_Flag_Hex_Display = !this->_Flag_Hex_Display;
}

// 下拉列表框 选择远程主机 ip 地址与端口号
void NetAssist::on_comboBox_IpPort_currentTextChanged(const QString &arg1)
{
    qDebug() << arg1;
    // 初始化远程 ip 地址与端口号
    this->_Ip.clear();
    this->_Port = 0;
    // 判断远程 ip 地址与端口号是否为空
    if (arg1 == nullptr)
    {
        // 为空
        this->_flag_IpPort = true;
    }
    else
    {
        // 不为空
        this->_flag_IpPort = false;
    }
    // 判断是否群发
    if (arg1 == " ALL Connections")
    {
        // 群发
        this->_Group_Send = true;
    }
    // 判断是否单发
    if (!this->_flag_IpPort && arg1 != " ALL Connections")
    {
        // 切割字符串获取远程 ip 地址与端口号
        QStringList list1 = arg1.split(":");
        this->_Ip = list1[0];
        this->_Port = list1[1].toInt();
        // 群发标志位复位
        this->_Group_Send = false;
    }
}

// 清除下拉列表框 选择远程主机 ip 地址与端口号
void NetAssist::on_pushButton_Clear_clicked()
{
    //用消息盒子提示
    int ret = QMessageBox::information(this,"提示","确定清除远程主机记录？", QMessageBox::Yes, QMessageBox::No);
    if(ret  == QMessageBox::Yes)
    {
        // 清除远程主机 ip 地址与端口号
        this->_IpPort.clear();
        // 清除下拉列表框
        ui->comboBox_IpPort->clear();
    }
}

// 十六进制发送
void NetAssist::on_checkBox_HexSend_clicked()
{
    // 每次点击改变十六进制发送按钮状态
    this->_Flag_Hex_Send = !this->_Flag_Hex_Send;
    // 获取要发送的数据
    QString msg = ui->textEdit_Send->toPlainText();
    // 是否启用文件数据源
    if (this->_Send_File)
    {
        // 文件数据源赋值
        this->_Hex_Send_Str = this->_Send_File_Data;
        return;
    }
    // 判断发送文本框是否为空
    if (msg == nullptr)
    {
        // 用消息盒子提示
        QMessageBox::warning(this,"警告","发送文本框为空。", QMessageBox::Ok);
        // 不勾选
        ui->checkBox_HexSend->setChecked(false);
        return;
    }
    // 字符串转十六进制
    if (this->_Flag_Hex_Send)
    {
        // 保存备份
        this->_Hex_Send_Str.clear();
        this->_Hex_Send_Str = msg;
        // 清除发送文本框
        ui->textEdit_Send->clear();
        // 字符串转十六进制并显示在发送文本框
        ui->textEdit_Send->insertPlainText(String_To_Hex(msg));
    }
    // 十六进制转字符串
    else
    {
        // 清除发送文本框
        ui->textEdit_Send->clear();
        // 字符串转十六进制并显示在发送文本框
        ui->textEdit_Send->insertPlainText(QByteArray::fromHex(QString(msg).toUtf8()));
    }
}

// 复位计数
void NetAssist::on_pushButton_ResetCount_clicked()
{
    // 清空发送、接收计数
    this->_TX_Count = 0;
    this->_RX_Count = 0;
    ui->label_TX->clear();
    ui->label_RX->clear();
    // 发送、接收计数显示
    ui->label_TX->setText(QString("TX:%1").arg(this->_TX_Count));
    ui->label_RX->setText(QString("RX:%1").arg(this->_RX_Count));
}

// 显示接收时间
void NetAssist::on_checkBox_TimeDisplay_clicked()
{
    // 每次点击改变状态
    this->_Display_Time = !this->_Display_Time;
    // 显示接收时间
    if (this->_Display_Time)
    {
        // 启动换行
        this->_Flag_Auto_Line = true;
        // 不可编辑
        ui->checkBox->setEnabled(false);
        // 勾选
        ui->checkBox->setChecked(true);
    }
    else
    {
        // 取消换行
        this->_Flag_Auto_Line = false;
        // 可编辑
        ui->checkBox->setEnabled(true);
        // 不勾选
        ui->checkBox->setChecked(false);
    }
}

// 暂停接收显示
void NetAssist::on_checkBox_StopDisplay_clicked()
{
    // 每次点击改变状态
    this->_Stop_Display = !this->_Stop_Display;
    // 暂停接收显示
    if (this->_Stop_Display)
    {
        ui->textEdit_Recv->setEnabled(false);
    }
    else
    {
        ui->textEdit_Recv->setEnabled(true);
    }
}

// 循环周期发送按钮
void NetAssist::on_checkBox_SendTime_clicked()
{
    // 每次点击改变状态
    this->_Loop_Send = !this->_Loop_Send;
    // 判断是否循环
    if (this->_Loop_Send)
    {
        // 创建时间对象
        this->_Timer = new QTimer(this);
        // 循环周期 xxx ms
        this->_Timer->start(this->_Send_Time.toInt());
        // 槽函数设置
        connect(this->_Timer, SIGNAL(timeout()), this, SLOT(Time_Send()));
    }
    else
    {
        // 删除时间对象
        delete this->_Timer;
    }
}

// 获取发送周期时间
void NetAssist::on_lineEdit_SendTime_textEdited(const QString &arg1)
{
    // 保存时间
    this->_Send_Time.clear();
    this->_Send_Time = arg1;
}

// 循环周期发送处理
void NetAssist::Time_Send()
{
    // 判断是否点击发送按钮
    if (this->_Send)
    {
        // 循环周期 xxx ms
        this->_Timer->start(this->_Send_Time.toInt());
        // 根据网络类型发送信息
        NetType_Send();
    }
    else
    {
        // 停止循环
        this->_Timer->stop();
    }
}

// 文件载入
void NetAssist::on_pushButton_SendData_clicked()
{
    // 文件载入处理
    Read_File_Data();
    // 发送文本框显示数据
    ui->textEdit_Send->setText(this->_Send_File_Data.data());
}

// 接收文本框自动往下滚动
void NetAssist::on_textEdit_Recv_textChanged()
{
    // 每次接收信息都把光标移动到末尾
    ui->textEdit_Recv->moveCursor(QTextCursor::End);
}

// 发送文本框自动往下滚动
void NetAssist::on_textEdit_Send_textChanged()
{
    // 每次发送信息都把光标移动到末尾
    ui->textEdit_Send->moveCursor(QTextCursor::End);
}

// 启用文件数据源
void NetAssist::on_checkBox_SendFile_clicked()
{
    // 每次点击改变状态
    this->_Send_File = !this->_Send_File;
    // 判断是否启用
    if (this->_Send_File)
    {
        // 启用文件数据源处理
        Send_File_Data();
    }
    else
    {
        // 清空发送文本框
        ui->textEdit_Send->clear();
        // 发送文本框可编辑
        ui->textEdit_Send->setEnabled(true);
    }
}

// 保存数据
void NetAssist::on_pushButton_SaveData_clicked()
{
    // 保存数据处理
    Write_File_Data();
}

// 接收转向文件
void NetAssist::on_checkBox_RecvFile_clicked()
{
    // 每次点击改变状态
    this->_Recv_File = !this->_Recv_File;
    // 判断是否接收转向文件
    if (this->_Recv_File)
    {
        // 获取文件绝对路径
        this->_Recv_FileName = QFileDialog::getOpenFileName(this, "选择或创建一个文件保存数据", nullptr, "All files(*);;Text files(*.txt)");
        // 接收文本框显示文件路径
        ui->textEdit_Recv->setText("接收转向至文件：");
        ui->textEdit_Recv->append(this->_Recv_FileName + "...");
        // 接收文本框不可编辑
        ui->textEdit_Recv->setEnabled(false);
    }
    else
    {
        // 清空接收文件数据
        this->_Recv_File_Data.clear();
        // 清空接收文本框
        ui->textEdit_Recv->clear();
        // 接收文本框可编辑
        ui->textEdit_Recv->setEnabled(true);
    }
}
